package com.gap.dam.module.business.search;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gap.dam.common.base.ResponseDTO;
import com.gap.dam.constant.ElasticSearchConst;
import com.gap.dam.module.business.search.domain.dto.Asset;
import com.gap.dam.module.business.search.domain.vo.AssetResultVO;
import com.gap.dam.module.business.search.domain.dto.AssetSearchDTO;
import com.gap.dam.util.*;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.ParsedValueCount;
import org.elasticsearch.search.aggregations.metrics.ValueCountAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class AssetSearchService extends BaseElasticSearchService {

    private static Map<String, String>  TAG_MAPPING = new HashMap<>();

    static {
        TAG_MAPPING.put("Photography", "Asset Type");
        TAG_MAPPING.put("Video/Audio", "Asset Type");
        TAG_MAPPING.put("JPEG", "File Type");
        TAG_MAPPING.put("2021", "Year");
        TAG_MAPPING.put("Fall", "Season");
        TAG_MAPPING.put("Summer", "Season");
        TAG_MAPPING.put("Summer", "Season");
        TAG_MAPPING.put("Spring", "Season");
        TAG_MAPPING.put("Expired", "Usage Expiration Date");
        TAG_MAPPING.put("Available", "Usage Expiration Date");
        TAG_MAPPING.put("Single", "Model");
        TAG_MAPPING.put("Couple", "Model");
        TAG_MAPPING.put("Family", "Model");
        TAG_MAPPING.put("Heavy Gauge", "Collection");
        TAG_MAPPING.put("Kila Gauge", "Collection");
    }

    /**
     * 创建索引
     * @param index
     */
    public void createIndex(String index) {
        super.createIndexRequest(index);
    }

    public void deleteIndex(String index) {
        super.deleteIndexRequest(index);
    }

    public void insert(String index, List<Asset> assetList) {
        try {
            assetList.forEach(asset -> {
                IndexRequest indexRequest = buildIndexRequest(index, String.valueOf(asset.getId()), asset);
                try {
                    client.index(indexRequest, COMMON_REQUEST_OPTIONS);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        } finally {
            try {
                client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void update(String index, List<Asset> assetList) {
        assetList.forEach(asset -> {
            updateIndexReuqest(index, String.valueOf(asset.getId()), asset);
        });
    }

    public void delete(String index, Asset asset) {
        if (ObjectUtils.isNull(asset)) {
            throw new IllegalArgumentException("asset must not null");
        }

        deleteIndexRequest(index, String.valueOf(asset.getId()));
    }

    public void deleteAll(String index) {
        List<Asset> assets = searchAll(index);
        assets.forEach(asset -> {
            deleteIndexRequest(index, String.valueOf(asset.getId()));
        });
    }

    public List<Asset> searchAll(String index) {
        SearchResponse searchResponse = search(index);
        SearchHit[] hits = searchResponse.getHits().getHits();
        List<Asset> assets = new ArrayList<>();
        Arrays.stream(hits).forEach(hit -> {
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            Asset asset = BeanUtil.mapToBean(sourceAsMap, Asset.class, true);
            assets.add(asset);
        });

        return assets;
    }

    /**
     * 分页查询
     * @param assetSearchDTO
     * @return
     */
    public ResponseDTO<ESSearchResultDTO<AssetResultVO>> searchAssetByPage(AssetSearchDTO assetSearchDTO) {
        Page page = PageUtil.convert2QueryPage(assetSearchDTO);
        SearchResponse searchResponse = searchByPage(ElasticSearchConst.INDEX_NAME, assetSearchDTO);
        SearchHit[] hits = searchResponse.getHits().getHits();
        // 设置查询结果总数
        page.setTotal(searchResponse.getHits().getTotalHits().value);
        // 计算分页数
        page.setPages((page.getTotal() - 1) / page.getSize() + 1);
        List<AssetResultVO> resultVOList = this.convert2AssetResultVOList(hits);
        // 处理聚合结果
        List<TagTypeDTO> tagTypeDTOList = this.getTagAggreationResult(searchResponse);

        // 过滤出Category列表
        List<AssetResultVO> categoryResultVOList = resultVOList.stream().filter(e -> "category".equals(e.getFileType())).collect(Collectors.toList());
        // 过滤出非Category列表
        List<AssetResultVO> assetResultVOList = resultVOList.stream().filter(e -> !"category".equals(e.getFileType())).collect(Collectors.toList());
        categoryResultVOList.forEach(e -> {
            e.setPictureUrl(assetResultVOList.stream().findAny().orElse(new AssetResultVO()).getUrl());
        });
        page.setRecords(resultVOList);
        ESSearchResultDTO<AssetResultVO> esSearchResultDTO = PageUtil.convert2ESSearchResult(page);
        esSearchResultDTO.setTagTypeList(tagTypeDTOList);
        return ResponseDTO.succData(esSearchResultDTO);
    }

    public ResponseDTO<ESSearchResultDTO<AssetResultVO>> searchByPage(AssetSearchDTO assetSearchDTO) {
        ESSearchResultDTO<AssetResultVO> esSearchResultDTO = new ESSearchResultDTO<>();
        Page page = PageUtil.convert2QueryPage(assetSearchDTO);
        // 如果仅仅传入Category查询条件
        if (StringUtils.isNotEmpty(assetSearchDTO.getCategory())
            && StringUtil.isEmpty(assetSearchDTO.getName())
            && (assetSearchDTO.getTags() == null || assetSearchDTO.getTags().size() == 0)) {
            // 查询指定Category下的Assets和Categorys
            SearchResponse searchResponse = super.searchPageByCategoryId(ElasticSearchConst.INDEX_NAME, assetSearchDTO);
            SearchHit[] hits = searchResponse.getHits().getHits();
            // 设置查询结果总数
            page.setTotal(searchResponse.getHits().getTotalHits().value);
            // 计算分页数
            page.setPages((page.getTotal() - 1) / page.getSize() + 1);
            List<AssetResultVO> assetWithCategoryList = convert2AssetResultVOList(hits);

            // 查询指定Category下及其子Category下的所有Assets以及聚合结果
            SearchResponse searchResponseMore = super.searchPageByMultiCondition(ElasticSearchConst.INDEX_NAME, assetSearchDTO);
            List<AssetResultVO> assetWithoutCategoryList = this.convert2AssetResultVOList(searchResponseMore.getHits().getHits());
            List<TagTypeDTO> tagTypeDTOList = this.getTagAggreationResult(searchResponseMore);

            // 处理Category背景图片
            assetWithCategoryList
                    .stream()
                    .filter(a -> "category".equals(a.getFileType()))
                    .forEach(asset -> {
                String pictureUrl = assetWithoutCategoryList.stream()
//                                    .filter(e -> e.getParentCategoryIdList().contains(asset.getCategoryId()))
                                    .findAny().orElse(new AssetResultVO()).getUrl();
                asset.setPictureUrl(pictureUrl);
            });
            page.setRecords(assetWithCategoryList);
            esSearchResultDTO = PageUtil.convert2ESSearchResult(page);
            esSearchResultDTO.setTagTypeList(tagTypeDTOList);
        } else if (StringUtils.isNotEmpty(assetSearchDTO.getName())
            || (assetSearchDTO.getTags() != null && assetSearchDTO.getTags().size() != 0 )) {
            // 传入了多个组合查询条件
            // 查询指定Category下的Assets和Categorys
            SearchResponse searchResponse = super.searchPageByMultiCondition(ElasticSearchConst.INDEX_NAME, assetSearchDTO);
            SearchHit[] hits = searchResponse.getHits().getHits();
            // 设置查询结果总数
            page.setTotal(searchResponse.getHits().getTotalHits().value);
            // 计算分页数
            page.setPages((page.getTotal() - 1) / page.getSize() + 1);
            List<AssetResultVO> assetResultVOList = convert2AssetResultVOList(hits);

            // 处理聚合结果
            List<TagTypeDTO> tagTypeDTOList = this.getTagAggreationResult(searchResponse);

            page.setRecords(assetResultVOList);
            esSearchResultDTO = PageUtil.convert2ESSearchResult(page);
            esSearchResultDTO.setTagTypeList(tagTypeDTOList);
        }
        return ResponseDTO.succData(esSearchResultDTO);
    }

    /**
     * 将ES查询结果列表转换成AssetResultVO列表
     * @param hits
     * @return
     */
    private List<AssetResultVO> convert2AssetResultVOList(SearchHit[] hits) {
        List<Asset> assets = new ArrayList<>();
        Arrays.stream(hits).forEach(hit -> {
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            Asset asset = BeanUtil.mapToBean(sourceAsMap, Asset.class, true);
            assets.add(asset);
        });
        return assets.stream().map(e -> com.gap.dam.util.BeanUtil.copy(e, AssetResultVO.class)).collect(Collectors.toList());
    }

    /**
     * 获取查询结果聚合
     * @return
     */
    private List<TagTypeDTO> getTagAggreationResult(SearchResponse searchResponse) {
        Map<String, TagTypeDTO> existsTagTypeMap = new HashMap<>();
        List<TagTypeDTO> tagTypeDTOList = Lists.newArrayList();
        ParsedStringTerms termsName = searchResponse.getAggregations().get("group_by_tags");
        List<? extends Terms.Bucket> buckets = termsName.getBuckets();
        buckets.forEach(name -> {
            String key = (String) name.getKey();
            TagCountDTO tagCountDTO = new TagCountDTO();
            tagCountDTO.setTagName(StringUtils.capitalize(key));
            tagCountDTO.setDocCount((int) name.getDocCount());

            String tagType = TAG_MAPPING.get(tagCountDTO.getTagName());
            if (StringUtils.isNotEmpty(tagType)) {
                if (existsTagTypeMap.containsKey(tagType)) {
                    TagTypeDTO tagTypeDTO = existsTagTypeMap.get(tagType);
                    List<TagCountDTO> tagCountDTOList = tagTypeDTO.getTagList();
                    if (tagCountDTOList == null) {
                        tagCountDTOList = new ArrayList<>();
                        tagTypeDTO.setTagList(tagCountDTOList);
                    }
                    tagCountDTOList.add(tagCountDTO);
                } else {
                    TagTypeDTO tagTypeDTO = new TagTypeDTO();
                    tagTypeDTO.setTagType(tagType);
                    List<TagCountDTO> tagCountDTOList = new ArrayList<>();
                    tagCountDTOList.add(tagCountDTO);
                    tagTypeDTO.setTagList(tagCountDTOList);
                    tagTypeDTOList.add(tagTypeDTO);
                    existsTagTypeMap.put(tagType, tagTypeDTO);
                }
            }
        });
        return tagTypeDTOList;
    }


    public void groupByTags() {
        try {
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            TermsAggregationBuilder fieldBuilder = AggregationBuilders.terms("group_by_tags").field("tags");
            ValueCountAggregationBuilder countField = AggregationBuilders.count("count_value").field("tags");
            fieldBuilder.subAggregation(countField);
            searchSourceBuilder.aggregation(fieldBuilder);
            SearchRequest searchRequest = new SearchRequest(ElasticSearchConst.INDEX_NAME).source(searchSourceBuilder);

            SearchResponse searchResponse = client.search(searchRequest, COMMON_REQUEST_OPTIONS);
            ParsedStringTerms termsName = searchResponse.getAggregations().get("group_by_tags");
            List<? extends Terms.Bucket> buckets = termsName.getBuckets();
            buckets.forEach(name -> {
                String key = (String) name.getKey();
                ParsedValueCount countName = name.getAggregations().get("count_value");
                double value = countName.value();
                System.out.println(String.format("name, count %s %f %d", key, value, name.getDocCount()));
            });
        } catch (Exception ex) {
            ex.printStackTrace();
        }


    }
}
